X001 圆角圆形 ImageView

鉴于 Android 提供的 ImageView 控件已经非常完善,不仅处理好了各种缩放问题,而且有着很好的兼容性,还实现了显示图片等一系列的任务,我们只要在它的基础上添加一个显示圆角圆形的功能即可。

一、属性配置

首先创建一个 res/values/attrs.xml 文件:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundImageView">
<attr name="type" format="enum">
<enum name="circle" value="1"/>
<enum name="round" value="2"/>
</attr>
<attr name="radius" format="dimension"/>
</declare-styleable>
</resources>

二、代码实现

1. 思路

  • 定义一个 Java 类 RoundImageView,继承自 ImageView,重写其构造方法,获取配置属性进行初始化。
  • 重写 onMeasure 方法,当模式为圆形的时候使其控件的宽高一致。
  • 重写 onDraw 方法,增加两种模式处理逻辑。

2. 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package com.xxt.xtest;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import androidx.appcompat.widget.AppCompatImageView;
public class RoundImageView extends AppCompatImageView {
private static final int MODE_NONE = 0; // 普通模式,默认
private static final int MODE_CIRCLE = 1; // 圆形模式
private static final int MODE_ROUND = 2; // 圆角矩形模式
private int currMode = MODE_NONE;
private int currRound = dp2px(10); // 圆角半径
private Paint mPaint;
private RectF mRectF = null;
private Drawable mPreDrawable = null;
public RoundImageView(Context context) {
super(context);
initViews();
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
obtainStyledAttrs(context, attrs, defStyleAttr);
initViews();
}
private void obtainStyledAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView,
defStyleAttr, 0);
currMode = a.getInt(R.styleable.RoundImageView_type, MODE_NONE);
currRound = a.getDimensionPixelSize(R.styleable.RoundImageView_radius, currRound);
a.recycle();
}
private void initViews() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mRectF = new RectF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (currMode == MODE_CIRCLE) {
int result = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(result, result);
}
}
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
Matrix drawMatrix = getImageMatrix();
if (drawable == null) {
return; // couldn't resolve the URI
}
if (drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
return; // nothing to draw (empty bounds)
}
if (drawMatrix == null && getPaddingTop() == 0 && getPaddingLeft() == 0) {
drawable.draw(canvas);
} else {
final int saveCount = canvas.getSaveCount();
canvas.save();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (getCropToPadding()) {
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),
scrollX + getRight() - getLeft() - getPaddingRight(),
scrollY + getBottom() - getTop() - getPaddingBottom());
}
}
canvas.translate(getPaddingLeft(), getPaddingTop());
// 当为圆形模式的时候
if (currMode == MODE_CIRCLE) {
setBitmapShader(drawable);
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, getWidth() / 2f, mPaint);
}
// 当为圆角模式的时候
else if (currMode == MODE_ROUND) {
setBitmapShader(drawable);
mRectF.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
canvas.drawRoundRect(mRectF, currRound, currRound, mPaint);
} else {
if (drawMatrix != null) {
canvas.concat(drawMatrix);
}
drawable.draw(canvas);
}
canvas.restoreToCount(saveCount);
}
}
private void setBitmapShader(Drawable drawable) {
// 防止多次重 new 对象
if (drawable != null && drawable != mPreDrawable) {
Bitmap bitmap = drawable2Bitmap(drawable);
mPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
mPreDrawable = drawable;
}
}
/**
* drawable 转换成 bitmap
*/
private Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable == null) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// 根据传递的 scaleType 获取 matrix 对象,设置给 bitmap
Matrix matrix = getImageMatrix();
if (matrix != null) {
canvas.concat(matrix);
}
drawable.draw(canvas);
return bitmap;
}
private int dp2px(float value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
getResources().getDisplayMetrics());
}
}

在 onDraw 方法中,以下代码是我们添加的逻辑,其余代码为 ImageView 源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Drawable drawable = getDrawable();
Matrix drawMatrix = getImageMatrix();
...
// 当为圆形模式的时候
if (currMode == MODE_CIRCLE) {
setBitmapShader(drawable);
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, getWidth() / 2f, mPaint);
}
// 当为圆角模式的时候
else if (currMode == MODE_ROUND) {
setBitmapShader(drawable);
mRectF.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
canvas.drawRoundRect(mRectF, currRound, currRound, mPaint);
}

3. 在布局文件中使用

效果如首图所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"/>
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
android:scaleType="fitXY" />
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
android:scaleType="centerCrop" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
app:type="round"/>
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
android:scaleType="fitXY"
app:type="round"/>
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
android:scaleType="centerCrop"
app:type="round"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
app:type="circle"/>
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
android:scaleType="fitXY"
app:type="circle"/>
<com.xxt.xtest.RoundImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="20dp"
android:src="@drawable/meinv"
android:scaleType="centerCrop"
app:type="circle"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp">
<com.xxt.xtest.RoundImageView
android:layout_margin="10dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@color/colorAccent" />
<com.xxt.xtest.RoundImageView
android:layout_margin="10dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@color/colorAccent"
app:type="round" />
<com.xxt.xtest.RoundImageView
android:layout_margin="10dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@color/colorAccent"
app:type="circle" />
</LinearLayout>
</LinearLayout>